
#include <Liberty3D/Library/Task.h>
#include <Liberty3D/Library/TaskScheduler.h>

#include <stdexcept>

using namespace Liberty::Engine;
using namespace Liberty::Library;

Task::Task() :
    _priority (0),
    _options  (0), 
    _exception(nullptr) {
}

Task::~Task() {
    if (_exception) {
        delete _exception;
    }
}

bool Task::execute() {
    std::vector<boost::any> params;
    
    {
        boost::mutex::scoped_lock guard(TaskScheduler::_mutex);

        params = _parameters;
    }

    boost::any result;
    
    try {
        result = _method->call(params);
    }
    catch (std::logic_error& ex) {
        _exception = new std::logic_error(ex.what());
    }
    catch (std::runtime_error& ex) {
        _exception = new std::runtime_error(ex.what());
    }
    catch (std::exception& ex) {
        _exception = new std::runtime_error(ex.what());
    }
    catch (...) {
        _exception = new std::runtime_error("Segmentation fault");
    }

    {
        boost::mutex::scoped_lock guard(TaskScheduler::_mutex);

        _result = result;
        _options &= ~TaskStatusBits;

        if (_options & TaskRecurring) {
            return true;
        }

        _options |= TaskFinished;
        return false;
    }
}

void Task::setPriority(int p) {
    if (_priority == p) {
        return;
    }

    boost::mutex::scoped_lock guard(TaskScheduler::_mutex);

    _priority = p;
    TaskScheduler::reorder();
}

void Task::setOptions(unsigned int o) {
    boost::mutex::scoped_lock guard(TaskScheduler::_mutex);

    _options = (_options & TaskStatusBits) | (o & ~TaskStatusBits);
}

void Task::setMethod(const std::shared_ptr<Liberty::Engine::IObjectMethod>& method) {
    boost::mutex::scoped_lock guard(TaskScheduler::_mutex);

    _method = method;
}

const boost::any& Task::result() const {
    boost::mutex::scoped_lock guard(TaskScheduler::_mutex);

    return _result;
}

void Task::addParameter(const boost::any& p) {
    boost::mutex::scoped_lock guard(TaskScheduler::_mutex);

    _parameters.push_back(p);
}

void Task::addParameter(boost::any&& p) {
    boost::mutex::scoped_lock guard(TaskScheduler::_mutex);

    _parameters.push_back(p);
}

void Task::addDependancy(const std::shared_ptr<Task>& t) {
    if (t.get() == this) {
        throw std::invalid_argument("Task::addDependancy: Can't depends on itself...");
    }

    boost::mutex::scoped_lock guard(TaskScheduler::_mutex);

    _dependsOn.push_back(t);
}

bool Task::isReady() const {
    std::vector<std::shared_ptr<Task>>::const_iterator it, end(_dependsOn.end());

    for (it = _dependsOn.begin(); it != end; ++it) {
        if (!(*it)->hasFinished()) {
            return false;
        }
    }

    return true;
}

void Task::waitUntilFinished() {
    bool performWait   = false;
    bool performInline = false;

    while (true)
    {
        boost::mutex::scoped_lock guard(TaskScheduler::_mutex);

        if ((_options & TaskFinished) == TaskFinished) {
            return;
        }

        if ((_options & TaskRunning) == TaskRunning) {
            performWait = true;
        }
        else {
            performInline = true;
            _options     |= TaskRunning;
        }

        TaskScheduler::waitForTaskEvent(guard);
    }

    throw std::runtime_error("TODO");
}
